package routehb

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	"gitee.com/gitee-go/core"
	"gitee.com/gitee-go/core/bean/hbtpBean"
	"gitee.com/gitee-go/core/common"
	"gitee.com/gitee-go/core/model/pipeline"
	"gitee.com/gitee-go/core/runtime"
	"gitee.com/gitee-go/server/comm"
	"gitee.com/gitee-go/server/engine"
	comm2 "gitee.com/gitee-go/server/engine/comm"
	"gitee.com/gitee-go/utils"
	hbtp "github.com/mgr9525/HyperByte-Transfer-Protocol"
)

type RunnerRPC struct{} // server自己实现的RPC接口，供runner调用

// 安全校验
func (RunnerRPC) AuthFun() hbtp.AuthFun {
	return servAuthToken
}

// 任务拉取
func (RunnerRPC) JobPull(c *hbtp.Context) {
	hdr, err := c.ReqHeader()
	if err != nil {
		c.ResString(hbtp.ResStatusErr, "header err")
		return
	}
	for i := 0; i < 30; i++ {
		job := engine.Mgr().GetBuildEgn().PullJob(hdr.RelIp)
		if job != nil {
			job.Lock()
			c.ResJson(hbtp.ResStatusOk, job.Runjb())
			job.Unlock()
			core.Log.Debug("Engine jobPull resJson:", job.Job().Id)
			return
		}
		time.Sleep(time.Millisecond * 100)
	}
	c.ResString(hbtp.ResStatusNotFound, "the job list is empty")
}

// 查询build状态
func (RunnerRPC) BuildStatus(c *hbtp.Context, bdid string) {
	if bdid == "" {
		c.ResString(hbtp.ResStatusErr, "params err")
		return
	}
	stat, ok := engine.Mgr().GetBuildEgn().GetTaskStat(bdid)
	if !ok {
		c.ResString(hbtp.ResStatusNotFound, "no")
		return
	}
	c.ResJson(hbtp.ResStatusOk, stat)
}

// 更新任务状态
func (RunnerRPC) JobUpdate(c *hbtp.Context, info *runtime.ReqJobUpdate) {
	err := engine.Mgr().GetBuildEgn().JobUpdate(info)
	if err != nil {
		if err == comm2.BuildErrNoJob {
			c.ResString(hbtp.ResStatusNotFound, "update err:"+err.Error())
		} else {
			c.ResString(hbtp.ResStatusErr, "update err:"+err.Error())
		}
		return
	}
	c.ResString(hbtp.ResStatusOk, "ok")
}

/*func (RunnerRPC) JobCmdGroup(c *hbtp.Context,m*hbtpBean.CmdGroupJson) {
	if m.Id==""||m.JobId==""||m.BuildId==""{
		c.ResString(hbtp.ResStatusErr, "param err")
		return
	}
	db:=comm.DBMain.GetDB()
	e:=&pipeline.TCmdGroup{
		Id: m.Id,
		BuildId: m.BuildId,
		JobId: m.JobId,
		Name: m.Name,
		Num: m.Num,
		Status: common.BUILD_STATUS_PENDING,
		Created: time.Now(),
	}
	_,err:=db.Insert(e)
	if err != nil {
		c.ResString(hbtp.ResStatusErr, "db err:"+err.Error())
		return
	}
	for i,v:=range m.Cmds{
		le:=&pipeline.TCmdLine{
			Id: v.Id,
			GroupId: e.Id,
			BuildId: m.BuildId,
			JobId: m.JobId,
			Num: i,
			Content:v.Conts,
			Status: common.BUILD_STATUS_PENDING,
			Created: time.Now(),
		}
		_,err=db.Insert(le)
	}
	c.ResString(hbtp.ResStatusOk,"ok")
}*/

// 更新任务命令状态
func (RunnerRPC) JobUpCmd(c *hbtp.Context, m *hbtpBean.CmdUpdate) {
	hdr, _ := c.ReqHeader()
	buildid, ok := hdr.GetString("buildid")
	if !ok || buildid == "" {
		c.ResString(hbtp.ResStatusErr, "param err:buildid")
		return
	}
	jobid, ok := hdr.GetString("jobid")
	if !ok || jobid == "" {
		c.ResString(hbtp.ResStatusErr, "param err:jobid")
		return
	}
	now := time.Now().Format(common.TimeFmt)
	db := comm.DBMain.GetDB()
	switch m.Fs {
	case 1:
		db.Exec("update `t_cmd_line` set status=?,started=? where id=? and job_id=?",
			common.BUILD_STATUS_RUNNING, now, m.Id, jobid)
		db.Exec("update `t_cmd_line` set status=?,finished=? where id!=? and job_id=? and status=?",
			common.BUILD_STATUS_OK, now, m.Id, jobid, common.BUILD_STATUS_RUNNING)

		db.Exec("update `t_cmd_group` set status=?,started=? where id=? and job_id=?",
			common.BUILD_STATUS_RUNNING, now, m.Gid, jobid)
		db.Exec("update `t_cmd_group` set status=?,finished=? where id!=? and job_id=? and status=?",
			common.BUILD_STATUS_OK, now, m.Gid, jobid, common.BUILD_STATUS_RUNNING)
	case 2:
		db.Exec("update `t_cmd_line` set status=?,finished=? where id=? and job_id=?", common.BUILD_STATUS_OK, now, m.Id, jobid)
	}
}

// 日志推送
func (RunnerRPC) JobLogJson(c *hbtp.Context, m *hbtpBean.CmdLogLineJson) {
	hdr, _ := c.ReqHeader()
	buildid, ok := hdr.GetString("buildid")
	if !ok || buildid == "" {
		c.ResString(hbtp.ResStatusErr, "param err:buildid")
		return
	}
	jobid, ok := hdr.GetString("jobid")
	if !ok || jobid == "" {
		c.ResString(hbtp.ResStatusErr, "param err:jobid")
		return
	}
	/*if hdr.GetBool("ispre"){
		flnm=jobid+"s"
	}
	now:=time.Now().Format(common.TimeFmt)
	db:=comm.DBMain.GetDB()
	if m.Type==hbtpBean.TypeCmdLogLineSysrun{
	//}else if m.Type==hbtpBean.TypeCmdLogLineCmd||m.Type==hbtpBean.TypeCmdLogLineCmderr{
	}else if m.Type==hbtpBean.TypeCmdLogLineCmdend{
	}else if m.Type==hbtpBean.TypeCmdLogLineSysend{
	}*/
	dir := filepath.Join(comm.WorkPath, comm.Build, buildid, comm.Jobs)
	logpth := filepath.Join(dir, fmt.Sprintf("%v.log", jobid))
	os.MkdirAll(dir, 0755)
	logfl, err := os.OpenFile(logpth, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
	if err != nil {
		c.ResString(hbtp.ResStatusErr, "OpenFile err:"+err.Error())
		return
	}
	defer logfl.Close()
	logfl.Write(c.BodyBytes())
	logfl.WriteString("\n")
	//fmt.Println("handleJobLog("+jobid.(string)+") get:", string(c.BodyBytes()))
	c.ResString(hbtp.ResStatusOk, "ok")
}

// 下载仓库代码
func (RunnerRPC) DownBuildRepo(c *hbtp.Context, buildid string) {
	if buildid == "" {
		c.ResString(hbtp.ResStatusErr, "param err:buildid")
		return
	}
	zippth := filepath.Join(comm.WorkPath, comm.Build, buildid, "repo.zip")
	stat, err := os.Stat(zippth)
	if err != nil {
		c.ResString(hbtp.ResStatusErr, "repo not found:"+err.Error())
		return
	}
	zipfl, err := os.OpenFile(zippth, os.O_RDONLY, 0)
	if err != nil {
		c.ResString(hbtp.ResStatusErr, "repo open err:"+err.Error())
		return
	}
	defer zipfl.Close()
	err = c.ResBytes(hbtp.ResStatusOk, hbtp.BigIntToByte(stat.Size(), 8))
	if err != nil {
		core.Log.Errorf("res err:%v", err.Error())
	}

	bts := make([]byte, 10240)
	for {
		rn, err := zipfl.Read(bts)
		if rn > 0 {
			wn, err := c.Conn().Write(bts[:rn])
			if err != nil || wn <= 0 {
				break
			}
		}
		if err != nil {
			break
		}
	}
}

/**
上传制品 (文件类型)
1. 判断参数
2. 判断数据库制品是否存在
3. 保存文件流到工作目录
4, 保存成功修改状态
*/
func (RunnerRPC) UploadArtPipe(c *hbtp.Context, m *utils.Map) {
	buildid := m.GetString("buildid")
	if buildid == "" {
		c.ResString(hbtp.ResStatusErr, "param err:buildid")
		return
	}
	artid := m.GetString("artid")
	if artid == "" {
		c.ResString(hbtp.ResStatusErr, "param err:artid")
		return
	}
	artsz, err := m.GetInt("art_size")
	if err != nil || artsz <= 0 {
		c.ResString(hbtp.ResStatusErr, "param err:art_size")
		return
	}

	tArt := &pipeline.TArtifact{}
	db := comm.DBMain.GetDB()
	ok, err := db.Where("id=?", artid).Get(tArt)
	if !ok || err != nil {
		c.ResString(hbtp.ResStatusErr, "not found TArtifact:"+artid)
		return
	}
	bld := &pipeline.TBuild{}
	ok, err = db.Where("id=?", tArt.BuildId).Get(bld)
	if !ok || err != nil {
		c.ResString(hbtp.ResStatusErr, "not found TBuild:"+tArt.BuildId)
		return
	}
	piev := &pipeline.TPipelineVersion{}
	ok, err = db.Where("id=?", bld.PipelineVersionId).Get(piev)
	if !ok || err != nil {
		c.ResString(hbtp.ResStatusErr, "not found TPipelineVersion:"+bld.PipelineVersionId)
		return
	}
	if tArt.Scope == "archive" {
		t := &pipeline.TArtifactArchive{}
		ok, err = comm.DBMain.GetDB().
			Where("build_id=? and repository=? and name=?", tArt.BuildId, tArt.Repository, tArt.Name).Get(t)
		if ok {
			c.ResString(hbtp.ResStatusErr, fmt.Sprintf("found archive:%s/%s already exist", t.Repository, t.Name))
			return
		}
	}
	tArt.Status = common.BUILD_STATUS_ERROR
	defer func() {
		tArt.Updated = time.Now()
		_, err := db.Cols("status", "updated").Where("id=?", tArt.Id).Update(tArt)
		if err != nil {
			core.Log.Errorf("handleUpArtPipe update TArtifact(%s) err:%v", tArt.Id, err)
		}
	}()

	dir := filepath.Join(comm.WorkPath, comm.Build, buildid, comm.Jobs)
	flpth := filepath.Join(dir, fmt.Sprintf("%s.art", artid))
	os.MkdirAll(dir, 0755)
	artfl, err := os.OpenFile(flpth, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0644)
	if err != nil {
		c.ResString(hbtp.ResStatusErr, "OpenFile err:"+err.Error())
		return
	}
	defer artfl.Close()
	c.ResString(hbtp.ResStatusOk, "ok")

	sz := int64(0)
	bts := make([]byte, 1024)
	for {
		n, err := c.Conn().Read(bts)
		if n > 0 {
			sz += int64(n)
			artfl.Write(bts[:n])
		}
		if err != nil {
			break
		}
	}

	artfl.Close()
	tArt.Status = common.BUILD_STATUS_OK
	if sz != artsz {
		os.RemoveAll(flpth)
		tArt.Status = common.BUILD_STATUS_ERROR
		core.Log.Errorf("handleUpArtPipe size err:%d/%d", sz, artsz)
		//c.ResString(hbtp.ResStatusErr, fmt.Sprintf("save file size err:%d/%d", sz, artsz))
		return
	}

	if tArt.Scope == "archive" {
		ne := &pipeline.TArtifactArchive{
			Xid:        utils.NewXid(),
			RepoId:     piev.RepoId,
			BuildId:    tArt.BuildId,
			JobId:      tArt.JobId,
			StageId:    tArt.StageId,
			ArtId:      tArt.Id,
			Repository: tArt.Repository,
			Name:       tArt.Name,
			Created:    time.Now(),
		}
		comm.DBMain.GetDB().Insert(ne)
	}

}

// 上传制品 (变量类型)
func (RunnerRPC) UploadArtVar(c *hbtp.Context, m *utils.Map) {
	buildid := m.GetString("buildid")
	if buildid == "" {
		c.ResString(hbtp.ResStatusErr, "param err:buildid")
		return
	}
	jobid := m.GetString("jobid")
	if jobid == "" {
		c.ResString(hbtp.ResStatusErr, "param err:jobid")
		return
	}
	vars, ok := m.Get("vars")
	if !ok {
		c.ResString(hbtp.ResStatusErr, "param err:vars")
		return
	}
	bts, err := json.Marshal(vars)
	if err != nil {
		c.ResString(hbtp.ResStatusErr, "json err:"+err.Error())
		return
	}
	dir := filepath.Join(comm.WorkPath, comm.Build, buildid, comm.Jobs)
	flpth := filepath.Join(dir, fmt.Sprintf("%s.json", jobid))
	os.MkdirAll(dir, 0755)
	err = ioutil.WriteFile(flpth, bts, 0644)
	if err != nil {
		c.ResString(hbtp.ResStatusErr, "OpenFile err:"+err.Error())
		return
	}
	c.ResString(hbtp.ResStatusOk, "ok")
}

/**
下载制品 (文件类型)
1. 根据制品类型查找制品
2. 查找制品文件
3. 回复runner,并传输文件流
*/
func (RunnerRPC) GetArtPipe(c *hbtp.Context, m *utils.Map) {
	buildid := m.GetString("buildid")
	if buildid == "" {
		c.ResString(hbtp.ResStatusErr, "param err:buildid")
		return
	}
	jobid := m.GetString("jobid")
	if jobid == "" {
		c.ResString(hbtp.ResStatusErr, "param err:jobid")
		return
	}
	/*artid := hd.Get("artid")
	if !ok || artid == "" {
		c.ResString(hbtp.ResStatusErr, "param err:artid")
		return
	}*/
	artTyp := m.GetString("art_type")
	nmArt := m.GetString("art_name")
	nmStage := m.GetString("art_stage")
	nmJob := m.GetString("art_job")
	artRepo := m.GetString("art_repo")
	if artTyp == "" {
		c.ResString(hbtp.ResStatusErr, "param err:type")
		return
	}
	if nmArt == "" {
		c.ResString(hbtp.ResStatusErr, "param err:name")
		return
	}

	var ok bool
	var err error
	var dir string
	var flpth string
	/*srcJob, ok := engine.Mgr().GetBuildEgn().GetSyncJob(buildid, nmStage, nmJob)
	if ok {
		srcJob.Lock()
		for _, v := range srcJob.Job().Artifacts {
			if v.Scope == "pipieline" && v.Name == nmArt {
				dir = filepath.Join(comm.WorkPath, comm.Build, buildid, comm.Jobs)
				flpth = filepath.Join(dir, fmt.Sprintf("%s.art", v.Id))
				break
			}
		}
		srcJob.Unlock()
	}*/

	db := comm.DBMain.GetDB()
	bld := &pipeline.TBuild{}
	ok, err = db.Where("id=?", buildid).Get(bld)
	if !ok || err != nil {
		c.ResString(hbtp.ResStatusErr, "not found TBuild:"+buildid)
		return
	}
	arts := &pipeline.TArtifact{}
	if artTyp == "archive" {
		t := &pipeline.TArtifactArchive{}
		ok, _ = db.Where("build_id=? and repository=? and name=?", buildid, artRepo, nmArt).Get(t)
		if !ok {
			piev := &pipeline.TPipelineVersion{}
			ok, _ = db.Where("id=?", bld.PipelineVersionId).Get(piev)
			if ok {
				ok, _ = db.Where("repo_id=? and repository=? and name=?", piev.RepoId, artRepo, nmArt).Get(t)
			}
		}
		if ok {
			ok, _ = db.Where("id=? and status=?", t.ArtId, common.BUILD_STATUS_OK).Get(arts)
		}
	} else {
		ok, _ = db.Where("build_id=? and stage_name=? and job_name=? and scope=? and name=? and status=?",
			buildid, nmStage, nmJob, artTyp, nmArt, common.BUILD_STATUS_OK).Get(arts)
		core.Log.Debugf("GetArtPipe pipeType:%s,%s,%s,%s,%s", buildid, nmStage, nmJob, artTyp, nmArt)
	}
	core.Log.Debugf("GetArtPipe find artifact(ok:%v->%+v):type:%s,name:%s", ok, err, artTyp, nmArt)
	if ok && arts.Id != "" {
		dir = filepath.Join(comm.WorkPath, comm.Build, arts.BuildId, comm.Jobs)
		flpth = filepath.Join(dir, fmt.Sprintf("%s.art", arts.Id))
	}

	//下载逻辑
	if flpth == "" {
		c.ResString(hbtp.ResStatusErr, fmt.Sprintf("not found artifact(%s):%s", artTyp, nmArt))
		return
	}

	stat, err := os.Stat(flpth)
	if err != nil {
		c.ResString(hbtp.ResStatusErr, "artifact file not found")
		return
	}
	zipfl, err := os.OpenFile(flpth, os.O_RDONLY, 0)
	if err != nil {
		c.ResString(hbtp.ResStatusErr, "repo open err:"+err.Error())
		return
	}
	defer zipfl.Close()
	c.ResBytes(hbtp.ResStatusOk, hbtp.BigIntToByte(stat.Size(), 8))

	bts := make([]byte, 10240)
	for {
		rn, err := zipfl.Read(bts)
		if rn > 0 {
			wn, err := c.Conn().Write(bts[:rn])
			if err != nil || wn <= 0 {
				break
			}
		}
		if err != nil {
			break
		}
	}
}

// 获取制品 (变量类型)
func (RunnerRPC) GetArtEnvs(c *hbtp.Context, m *utils.Map) {
	buildid := m.GetString("buildid")
	if buildid == "" {
		c.ResString(hbtp.ResStatusErr, "param err:buildid")
		return
	}
	jobid := m.GetString("jobid")
	if jobid == "" {
		c.ResString(hbtp.ResStatusErr, "param err:jobid")
		return
	}
	stageid := m.GetString("stageid")
	if stageid == "" {
		c.ResString(hbtp.ResStatusErr, "param err:stageid")
		return
	}
	nmJob := m.GetString("jobnm")
	if nmJob == "" {
		c.ResString(hbtp.ResStatusErr, "param err:jobnm")
		return
	}
	srcJob, ok := engine.Mgr().GetBuildEgn().FindSyncJob(buildid, stageid, nmJob)
	if !ok {
		c.ResString(hbtp.ResStatusErr, "not found target job!!")
		return
	}

	dir := filepath.Join(comm.WorkPath, comm.Build, buildid, comm.Jobs)
	flpth := filepath.Join(dir, fmt.Sprintf("%v.json", srcJob.Job().Id))
	bts, err := ioutil.ReadFile(flpth)
	if err != nil {
		c.ResString(hbtp.ResStatusErr, "json open err:"+err.Error())
		return
	}
	c.ResBytes(hbtp.ResStatusOk, bts)
}
